package com.sunl.mall.ali.mq.autoconfigure;


import com.sunl.mall.ali.mq.autoconfigure.annotation.MqBatchConsumer;
import com.sunl.mall.ali.mq.autoconfigure.annotation.MqConsumer;
import com.sunl.mall.ali.mq.autoconfigure.annotation.MqOrderConsumer;
import com.sunl.mall.ali.mq.autoconfigure.bean.RocketMqBatchConsumerPostBean;
import com.sunl.mall.ali.mq.autoconfigure.bean.RocketMqConsumerPostBean;
import com.sunl.mall.ali.mq.autoconfigure.bean.RocketMqOrderConsumerPostBean;
import com.sunl.mall.ali.mq.autoconfigure.scanner.consumer.ClassPathConsumerScanner;
import com.sunl.mall.common.util.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * consumer 注册
 *
 * @date 2020/12/9 9:27
 */
@Slf4j
public class RocketMqConsumerBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    /**
     * 需要查询的包路径集合
     */
    private final Set<String> scanPackagePaths;
    /**
     * 需要进行后续处理的注解类
     */
    private Class<? extends Annotation> annotationClass;

    /**
     * 初始化processor
     *
     * @param annotationClass
     * @param scanPackagePaths
     */
    public RocketMqConsumerBeanPostProcessor(Class<? extends Annotation> annotationClass, Set<String> scanPackagePaths) {
        this.scanPackagePaths = scanPackagePaths;
        this.annotationClass = annotationClass;

        if (CollectionUtils.isEmpty(this.scanPackagePaths)) {
            if (log.isWarnEnabled()) {
                log.warn("scanPackagePaths is empty, MqConsumer Bean registry will be ignored.");
            }
            return;
        }
        if (this.annotationClass == null) {
            throw new IllegalArgumentException("annotationClass must not be null");
        }
    }

    /**
     * 注册消费者bean
     * <p>
     * 根据路径扫描需要注册的bean，该方法可以创建 {@link RocketMqConsumerPostBean},{@link RocketMqOrderConsumerPostBean},
     * {@link RocketMqBatchConsumerPostBean} 三种类型的bean对象。
     *
     * @param registry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        Class<?> consumerBeanClazz;
        if (this.annotationClass.isAssignableFrom(MqConsumer.class)) {
            consumerBeanClazz = RocketMqConsumerPostBean.class;
        } else if (this.annotationClass.isAssignableFrom(MqOrderConsumer.class)) {
            consumerBeanClazz = RocketMqOrderConsumerPostBean.class;
        } else if (this.annotationClass.isAssignableFrom(MqBatchConsumer.class)) {
            consumerBeanClazz = RocketMqBatchConsumerPostBean.class;
        } else {
            throw new IllegalArgumentException("annotationClass must be isAssignableFrom MqConsumer.class or MqOrderConsumer.class or MqBatchConsumer.class");
        }
        if (log.isDebugEnabled()) {
            log.debug("Searching for Mq order Consumer with @" + this.annotationClass.getSimpleName());
        }
        // 在classpath中搜索被注解的类
        ClassPathConsumerScanner scanner = new ClassPathConsumerScanner(registry);
        scanner.setBeanNameGenerator(this.resolveBeanNameGenerator(registry));
        scanner.setAnnotationClass(this.annotationClass);
        scanner.registerFilters();
        Map<String, List<RuntimeBeanReference>> groupListenerTmpBeanMap = scanner.doScanAndGet(StringUtils.toStringArray(scanPackagePaths));
        if (CollectionUtil.isNotEmpty(groupListenerTmpBeanMap)) {
            // 将搜索到的类初始化并注册到spring
            this.registerRocketMqConsumerBeans(groupListenerTmpBeanMap, registry, consumerBeanClazz);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        // 可以用来修改要注册的Bean定义，然后由 postProcessBeanDefinitionRegistry 注册到spring
    }

    /**
     * 注册consumer到spring上下文, bean的name是注解中的group值
     *
     * @param groupListenerTmpBeanMap
     * @param registry
     * @param consumerBeanClazz       mq实例class
     */
    private void registerRocketMqConsumerBeans(Map<String, List<RuntimeBeanReference>> groupListenerTmpBeanMap,
                                               BeanDefinitionRegistry registry,
                                               Class<?> consumerBeanClazz) {
        for (String groupValue : groupListenerTmpBeanMap.keySet()) {
            List<RuntimeBeanReference> subscriptionList = groupListenerTmpBeanMap.get(groupValue);
            RootBeanDefinition beanDefinition = new RootBeanDefinition();
            beanDefinition.setBeanClass(consumerBeanClazz);
            // 设置beanName属性的值为GROUP_ID
            beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue("beanName", groupValue));
            // 调用setSubscriptionRelation并传递临时bean list
            beanDefinition.getPropertyValues().addPropertyValue("subscriptionRelation", subscriptionList);
            // 设置初始化bean时自动调用start方法
            beanDefinition.setInitMethodName("start");
            // 设置销毁bean前自动调用shutdown方法
            beanDefinition.setDestroyMethodName("shutdown");
            // 将bean定义注册到spring环境，beanName就是groupValue
            registry.registerBeanDefinition(groupValue, beanDefinition);
        }
    }

    /**
     * 获取一个 beanName 生成器（方法借鉴 spring 的 ConfigurationClassPostProcessor）
     *
     * @param registry
     * @return
     */
    private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry registry) {
        // 默认使用短类名作为默认bean名称生成策略(这里因为需要支持springboot 1.x，所以就不用AnnotationBeanNameGenerator.INSTANCE了)
        BeanNameGenerator componentScanBeanNameGenerator = new AnnotationBeanNameGenerator();
        SingletonBeanRegistry sbr;
        if (registry instanceof SingletonBeanRegistry) {
            sbr = (SingletonBeanRegistry) registry;
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                componentScanBeanNameGenerator = generator;
            }
        }
        return componentScanBeanNameGenerator;
    }


    public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
    }
}